#pragma once
#include "core_headers.hpp"

#define LISTEN_DEFAULT_BACKLOG 1024
class Socket
{
public:
	Socket()
		: _sockfd(-1)
	{
	}
	Socket(int sockfd)
		: _sockfd(sockfd)
	{
	}
	~Socket()
	{
		Close();
	}
	int Fd()
	{
		return _sockfd;
	}
	// 向客服端链接新链接
	int Accept()
	{
		int newfd = ::accept(_sockfd, nullptr, nullptr); // 不关心链接信息
		if (newfd < 0)
		{
			ERR_LOG("accept error, %d%s", errno, strerror(errno));
			return -1;
		}
		return newfd;
	}
	// 创建服务端链接
	bool CreateTcpServer(uint16_t port, bool flag = false) // false为不开启非阻塞 true为开启阻塞
	{
		// 创建套接字 绑定地址信息  监听 设置非阻塞 开始地址复用
		if (!Create())
		{
			return false;
		}
		EnableAddressAndPortReuse();
		if (flag)
			SetNonBlock();
		if (!Bind(port))
		{
			return false;
		}
		if (!Listen())
		{
			return false;
		}
		return true;
	}
	// 创建客服端链接
	bool CreateTcpClient(const std::string &ip, uint16_t port)
	{
		// 创建套接字 发起链接
		if (!Create())
		{
			return false;
		}
		if (!Connect(ip, port))
		{
			return false;
		}
		return true;
	}
	ssize_t Recv(void *buf, size_t len, int flag = 0) // 默认是0 阻塞读取
	{
		ssize_t ret = ::recv(_sockfd, buf, len, flag);
		if (ret == 0)
		{
			INF_LOG("链接断开了，%d%s", errno, strerror(errno));
			return -1;
		}
		if (ret < 0)
		{
			// EAGAIN 和 EWOULDBLOCK 表示没有数据可读
			// EINTR 表示被信号中断
			if (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK)
			{
				return 0; // 表示这次接收没有接收到数据
			}
			ERR_LOG("recv error, %d%s", errno, strerror(errno));
			return -1;
		}
		return ret; // 返回读取数据长度
	}
	ssize_t Send(const void *buf, size_t len, int flag = 0)
	{
		ssize_t ret = ::send(_sockfd, buf, len, flag);
		if (ret < 0)
		{
			if (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK)
			{
				return 0; // 表示这次接收没有发送数据
			}
			ERR_LOG("send error, %d%s", errno, strerror(errno));
			return -1;
		}
		return ret; // 返回发送的数据长度
	}
	ssize_t RecvNonBlock(void *buf, size_t len)
	{
		return Recv(buf, len, MSG_DONTWAIT); // MSG_DONTWAIT 表示当前接收为非阻塞。
	}
	ssize_t SendNonBlock(const void *buf, size_t len)
	{
		if (len == 0)
			return 0;
		return Send(buf, len, MSG_DONTWAIT); // MSG_DONTWAIT 表示当前发送为非阻塞。
	}
	// 关闭套接字
	void Close()
	{
		if (_sockfd != -1)
		{
			::close(_sockfd);
			_sockfd = -1;
		}
	}

private:
	// 创建套接字
	bool Create()
	{
		_sockfd = ::socket(AF_INET, SOCK_STREAM, 0);
		if (_sockfd == -1)
		{
			ERR_LOG("socket error, %d%s", errno, strerror(errno));
			return false;
		}
		return true;
	}
	// 绑定地址信息
	bool Bind(uint16_t port)
	{
		struct sockaddr_in addr;
		memset(&addr, 0, sizeof(addr));
		addr.sin_family = AF_INET;
		addr.sin_port = ::htons(port);
		addr.sin_addr.s_addr = INADDR_ANY;
		socklen_t len = sizeof(addr);
		int ret = ::bind(_sockfd, (sockaddr *)&addr, len);
		if (ret < 0)
		{
			ERR_LOG("bind error, %d%s", errno, strerror(errno));
			return false;
		}
		return true;
	}
	// 监听
	bool Listen()
	{
		int ret = ::listen(_sockfd, LISTEN_DEFAULT_BACKLOG);
		if (ret < 0)
		{
			ERR_LOG("listen error, %d%s", errno, strerror(errno));
			return false;
		}
		return true;
	}
	// 向服务端发起链接
	bool Connect(const std::string &ip, uint16_t port)
	{
		struct sockaddr_in addr;
		memset(&addr, 0, sizeof(addr));
		addr.sin_family = AF_INET;
		addr.sin_port = ::htons(port);
		if (inet_pton(AF_INET, ip.c_str(), &addr.sin_addr) != 1)
		{
			ERR_LOG("无效ip, %d%s", errno, strerror(errno));
			return false;
		}
		socklen_t len = sizeof(addr);
		int ret = ::connect(_sockfd, (sockaddr *)&addr, len);
		if (ret < 0)
		{
			ERR_LOG("connect error, %d%s", errno, strerror(errno));
			return false;
		}
		return true;
	}
	// 开启地址端口复用
	void EnableAddressAndPortReuse()
	{
		int opt = 1;
		int ret = ::setsockopt(_sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int));
		if (ret < 0)
		{
			ERR_LOG("地址复用失败, %d%s", errno, strerror(errno));
			return;
		}
		opt = 1;
		ret = ::setsockopt(_sockfd, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(int));
		if (ret < 0)
		{
			ERR_LOG("端口复用失败, %d%s", errno, strerror(errno));
			return;
		}
	}
	// 设置非阻塞
	void SetNonBlock()
	{
		int flag = ::fcntl(_sockfd, F_GETFL, 0);
		if (flag < 0)
		{
			ERR_LOG("fcntl error, %d%s", errno, strerror(errno));
			return;
		}
		int ret = ::fcntl(_sockfd, F_SETFL, flag | O_NONBLOCK);
		if (ret < 0)
		{
			ERR_LOG("fcntl error, %d%s", errno, strerror(errno));
			return;
		}
	}

private:
	int _sockfd;
};